//制作图谱容器
import * as d3 from "d3";
import $ from "jquery";
// import $ from "jquery";

export function KgPainter(gid, graphInfo) {
  /** ---------- data ----------
   */
  this.gid = gid;
  this.gcontainer = {};
  this.svg = {};
  this.isFullscreen = graphInfo.isFullscreen;
  this.width = graphInfo.width;
  this.height = graphInfo.height;
  this.arrowMarker = {};
  this.simulation = {};
  this.qaGraphNode = {};
  this.qaButtonGroup = {};
  this.qaGraphNodeText = {};
  this.qaGraphLink = {};
  this.qaGraphLinkText = {};

  this.graphNode = {};
  this.graphNodeText = {};
  this.graphNodeButtonGroup = {};
  this.graphLink = {};
  this.graphLinkText = {};

  this.zoom = {};

  this.graph = {
    nodes: [],
    links: []
  };
  this.defaultR = 30;
  this.colorList = [
    "#ff8373",
    "#f9c62c",
    "#a5ca34",
    "#6fce7a",
    "#70d3bd",
    "#ea91b0"
  ];
  this.pagesizelist = [
    { size: 100, isactive: false },
    { size: 300, isactive: false },
    { size: 500, isactive: true },
    { size: 1000, isactive: false }
  ];
  this.toolbarData = [
    { name: "编辑", value: 1, code: "editBase" },
    { name: "展开", value: 1, code: "options" },
    { name: "追加", value: 1, code: "append" },
    { name: "连线", value: 1, code: "link" },
    { name: "删除", value: 1, code: "delete" }
  ];
  this.selectUuid = 0;
  this.nodeRecordList = [];
  this.default = {
    R: 30
  };
  /** ---------- function ----------
   */
  var context = {};
  this.initGraph = function(originalData) {
    context = this; //初始化GraphPainter上下文
    // let _this = this;

    //--- 初始化画布 ---
    initGraphContainer();
    addMaker();

    //--- 初始化数据 ---
    dataTransform(originalData);
    let data = formatData();
    let nodes = data.nodes;
    // let links = data.links;

    //--- 绘制节点 ---
    // drawToolButton(); //定义按钮组引用的use元素
    drawNode(nodes); // 更新节点
    console.log("-- finish drawNode --");
    drawNodeText(nodes); // 更新节点文字
    console.log("-- finish drawNodeText --");
    // drawButtonGroup(nodes); // 更新按钮组
    // console.log("-- finish drawButtonGroup --");
    // drawLink(links); // 更新连线 links
    // console.log("-- finish drawLink --");
    // drawLinkText(links); // 更新连线文字
    // console.log("-- finish drawLinkText --");

    this.updateGraph(data);

  };

  function initGraphContainer() {
    let _this = context;
    _this.gcontainer = d3.select(_this.gid);
    if (_this.isFullscreen) {
      _this.width = window.screen.width;
      _this.height = window.screen.height;
    } else {
      _this.width = $("#" + _this.pid).width();
      _this.height = $("#" + _this.pid).height();
    }
    _this.svg = _this.gcontainer.append("svg");
    var sWidth = _this.width;
    var sHeight = _this.height;
    _this.svg.style("background-color", _this.default.BACKGROUND_COLOR);
    _this.svg.attr("width", sWidth);
    _this.svg.attr("height", sHeight);
    _this.svg.attr("viewBox", "0 0 " + sWidth / 2 + " " + sHeight / 2);
    _this.svg.attr("id", "svg_idx");
    _this.svg.attr("preserveAspectRatio", "xMidYMidmeet");
    _this.simulation = d3
      .forceSimulation()
      .force("charge", d3.forceManyBody().strength(-1500))
      .force(
        "link",
        d3
          .forceLink()
          .distance(60)
          .id(function(d) {
            return d.uuid;
          })
      )
      .force("collide", d3.forceCollide().strength(-30))
      .force("center", d3.forceCenter(_this.width / 2, _this.height / 2));
    _this.qaGraphLink = _this.svg.append("g").attr("class", "line");
    _this.qaGraphNode = _this.svg.append("g").attr("class", "node");
    _this.qaGraphNodeText = _this.svg.append("g").attr("class", "nodetext");
    _this.nodebuttonGroup = _this.svg.append("g").attr("class", "nodebutton");
    _this.qaGraphLinkText = _this.svg.append("g").attr("class", "linetext");
    _this.svg.on(
      "click",
      function() {
        d3.selectAll(".buttongroup")
          .classed("notshow", true)
          .style("visibility", "hidden");
        // .style("display", "none")
      },
      false
    );
    console.log("--- finish initGraphContainer ---");
  }
  function addMaker() {
    let _this = context;
    var arrowMarker = _this.svg
      .append("marker")
      .attr("id", "arrow")
      .attr("markerUnits", "strokeWidth")
      .attr("markerWidth", "12") //
      .attr("markerHeight", "12")
      .attr("viewBox", "0 0 12 12")
      .attr("refX", "38")
      .attr("refY", "6")
      .attr("orient", "auto");
    var arrowPath = "M2,2 L10,6 L2,10 L6,6 L2,2"; // 定义箭头形状
    arrowMarker
      .append("path")
      .attr("d", arrowPath)
      .attr("fill", _this.default.MARKER_COLOR);
    console.log("--- finish addMaker ---");
  }

  function dataTransform(data) {
    let _this = context;
    _this.graph.id = data.kgID;
    // data = _this.getTestData();
    let nodes = data.nodeVOS;
    let links = data.tripleVOS;
    let typeList = [];
    nodes.forEach(function(node) {
      node.uuid = node.id;
      node.name = node.label_ZH;
      node.type = node.node_type;
      node.remberX = node.x;
      node.remberY = node.y;
      delete node.x;
      delete node.y;
      // delete node.id;
      // delete node.label_ZH;
      // delete node.node_type;
      if (node.color) return;
      typeList.forEach(function(type, index) {
        if (node.type == type) {
          node.color = _this.colorList[index];
        }
      });
      if (node.color == null) {
        typeList.push(node.type);
        node.color =
          typeList.length > _this.colorList.length
            ? _this.randomColor()
            : _this.colorList[typeList.length - 1];
      }
    });
    links.forEach(function(link) {
      link.uuid = link.id;
      link.name = link.predicate;
      link.sourceid = link.s_id;
      link.targetid = link.o_id;
      // delete link.id;
      // delete link.predicate;
      // delete link.s_id;
      // delete link.o_id;
    });
    _this.graph.nodes = nodes;
    _this.graph.links = links;
  }
  function formatData() {
    var _this = context;
    var lks = _this.graph.links;
    var nodes = _this.graph.nodes;
    nodes.forEach(function(n) {
      n.x = _this.width / 2;
      n.y = _this.height / 2;
      if (n.center === 1 || n.center === "1") {
        n.fx = _this.width / 2;
        n.fy = _this.height / 2;
      }
      if (typeof n.fx === "undefined" || n.fx === "") {
        n.fx = null;
      }
      if (typeof n.fy === "undefined" || n.fy === "") {
        n.fy = null;
      }
    });
    var links = [];
    lks.forEach(function(m) {
      var sourceNode = nodes.filter(function(n) {
        return n.uuid === m.sourceid;
      })[0];
      if (!sourceNode) return;
      var targetNode = nodes.filter(function(n) {
        return n.uuid === m.targetid;
      })[0];
      if (!targetNode) return;
      links.push({ source: sourceNode.uuid, target: targetNode.uuid, lk: m });
    });
    var data = {};
    data.nodes = nodes;
    data.links = formatLinkAttr(links);
    return data;
  }
  function formatLinkAttr(links) {
    var formatLinks = [];
    let linkGroupArr = [];
    links.forEach(function(link) {
      let added = false;
      for (let i = 0; i < linkGroupArr.length; i++) {
        if (
          (linkGroupArr[i][0].source == link.source &&
            linkGroupArr[i][0].target == link.target) ||
          (linkGroupArr[i][0].source == link.target &&
            linkGroupArr[i][0].target == link.source)
        ) {
          linkGroupArr[i].push(link);
          added = true;
          break;
        }
      }
      if (!added) {
        linkGroupArr.push([link]);
      }
    });
    linkGroupArr.forEach(function(group) {
      let groupSource = group[0].source;
      group.forEach(function(link, index) {
        link.linkDirection = link.source == groupSource;
        link.linkGroupIndex = index + 1;
        link.groupLinkTotal = group.length;
        formatLinks.push(link);
      });
    });
    formatLinks.forEach(function(item) {
      //线的单双，单数且小于2，则只有一条直线，大于2且单数，有多条弧线和一条直线
      var groupLinkTotalHalf = item.groupLinkTotal / 2;
      //能否被2整除，是否有余数，true=有，false=没有，有余数必然有一条直线
      item.groupLinkUneven = item.groupLinkTotal % 2 !== 0;
      //判断是否是最中间的直线（有余数，并且序号刚好是本组连线的一版，这里向上取整）
      item.groupLinkMiddle =
        item.groupLinkUneven === true &&
        Math.ceil(groupLinkTotalHalf) === item.linkGroupIndex;
      //假设有一条直线，index刚好是groupLinkTotalHalf的值，上方的曲线都为0，下方的曲线都为1
      var groupLinkLowerHalf = item.linkGroupIndex <= groupLinkTotalHalf;
      //上方的曲线和直线方向一致，下方的曲线和直线的方向相反
      item.groupLinkArcDirection =
        (groupLinkLowerHalf && item.linkDirection) ||
        (!groupLinkLowerHalf && !item.linkDirection)
          ? 0
          : 1;
      // item.groupLinkArcDirection = 1;
      //都在直线上方，则取当前序号，否则减去中间直线取序号
      item.groupLinkIndexCorrected = groupLinkLowerHalf
        ? item.linkGroupIndex
        : item.linkGroupIndex - Math.ceil(groupLinkTotalHalf);
      item.groupLinkMaxHalf = Math.round(item.groupLinkTotal / 2);
    });
    return formatLinks;
  }

  function drawNode(nodes) {
    var _this = context;
    var node = _this.qaGraphNode.selectAll("circle").data(nodes, function(d) {
      return d.uuid;
    });
    node.exit().remove();
    var nodeEnter = node.enter().append("circle");
    nodeEnter.on("click", onClickNode());
    nodeEnter.on("dblclick", function() {
      event.preventDefault();
    });
    nodeEnter.on("mouseenter", function() {
      d3.select(this).style("stroke-width", "6");
    });
    nodeEnter.on("mouseleave", onMouseleaveNode());
    nodeEnter.on("mouseover", onMouseoverNode());
    nodeEnter.call(
      d3
        .drag()
        .on("start", dragStarted)
        .on("drag", dragged)
        .on("end", dragEnded)
    );
    node = nodeEnter.merge(node).text(function(d) {
      return d.name;
    });
    node.style("fill", function(d) {
      return d.color;
    });
    node.style("stroke", _this.default.NODE_STROKE_COLOR);
    node.style("stroke-opacity", 0.6);
    node.style("stroke-width", 5);
    node.attr("r", _this.default.R);
    node.attr("uuid", function(d) {
      return d.uuid;
    });
    node
      .append("title") // 为每个节点设置title
      .text(function(d) {
        return d.name;
      });
    _this.graphNode = node;
  }
  function drawNodeText(nodes) {
    var _this = context;
    var nodetext = _this.qaGraphNodeText
      .selectAll("text")
      .data(nodes, function(d) {
        return d.uuid;
      });
    nodetext.exit().remove();
    var nodetextEnter = nodetext.enter().append("text");
    nodetextEnter.call(
      d3
        .drag()
        .on("start", dragStarted)
        .on("drag", dragged)
        .on("end", dragEnded)
    );
    nodetext = nodetextEnter.merge(nodetext).text(function(d) {
      return d.name;
    });
    let fontSize = function(d) {
      if (!d.font_size) {
        // 根据半径计算字体大小
        d.font_size = Math.floor(
          ((d.r ? d.r : _this.default.R) * 3) / (d.name.length * 1.5)
        );
        d.font_size = d.font_size > d.r - 5 ? d.r - 5 : d.font_size;
      }
      return d.font_size;
    };
    nodetext
      .style("fill", _this.default.FONT_COLOR)
      .style("pointer-events", "none") //取消鼠标对文字的感知
      .style("font-weight", function(d) {
        return Math.floor(900 / fontSize(d) + 500);
      })
      .attr("class", "nodetext")
      .attr("font-family", _this.default.FONT_FAMILY)
      .attr("text-anchor", "middle")
      .attr("dy", function(d) {
        return String(fontSize(d) / 2 - 2);
      })
      .attr("font-size", function(d) {
        return fontSize(d);
      })
      .attr("uuid", function(d) {
        return d.uuid;
      })
      .text(function(d) {
        return d.name;
      });
    nodetext
      .append("title") // 为每个节点设置title
      .text(function(d) {
        if (d.name) {
          return d.name;
        }
        return "";
      });
    _this.graphNodeText = nodetext;
  }

  function dragStarted(d) {
    if (!d3.event.active) context.simulation.alphaTarget(0.8).restart();
    d.fx = d.x;
    d.fy = d.y;
  }
  function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }
  function dragEnded(d) {
    if (!d3.event.active) context.simulation.alphaTarget(0);
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  function onClickNode() {
    return switchButtonShow;
  }
  function onMouseoverNode() {
    return showRelated;
  }
  function onMouseleaveNode() {
    return showAll;
  }

  function showAll() {
    d3.select(this).style("stroke-width", 2);
    //todo其他节点和连线一并显示
    d3.select(".node").style("fill-opacity", 1);
    d3.select(".nodetext").style("fill-opacity", 1);
    d3.selectAll(".link")
      .style("stroke-opacity", 1)
      .attr("marker-end", "url(#arrow)");
    d3.selectAll(".linktext").style("fill-opacity", 1);
  } //显示所有图元
  function showRelated(d) {
    let _this = context;
    //todo鼠标放上去只显示相关节点，其他节点和连线隐藏
    d3.selectAll(".node").style("fill-opacity", 0.1);
    var relvantNodeIds = [];
    var relvantNodes = _this.graph.links.filter(function(n) {
      return n.sourceid == d.uuid || n.targetid == d.uuid;
    });
    relvantNodes.forEach(function(item) {
      relvantNodeIds.push(item.sourceid);
      relvantNodeIds.push(item.targetid);
    });
    //显示相关的节点
    _this.qaGraphNode.selectAll("circle").style("fill-opacity", function(c) {
      if (relvantNodeIds.indexOf(c.uuid) > -1 || c.uuid == d.uuid) {
        return 1.0;
      }
    });
    //透明所有节点文字
    d3.selectAll(".nodetext").style("fill-opacity", 0.1);
    //显示相关的节点文字
    _this.qaGraphNodeText.selectAll("text").style("fill-opacity", function(c) {
      if (relvantNodeIds.indexOf(c.uuid) > -1 || c.uuid == d.uuid) {
        return 1.0;
      }
    });
    //透明所有连线、箭头
    d3.selectAll(".line")
      .style("stroke-opacity", 0.1)
      .selectAll("path")
      .attr("marker-end", "none");
    //显示相关的连线
    _this.qaGraphLink
      .selectAll(".link")
      .style("stroke-opacity", function(c) {
        if (c.lk.targetid === d.uuid || c.lk.sourceid === d.uuid) {
          return 1.0;
        }
      })
      .attr("marker-end", function(c) {
        //展示相关箭头
        if (c.lk.targetid === d.uuid || c.lk.sourceid === d.uuid) {
          return "url(#arrow)";
        }
      });
    //透明所有连线文字
    d3.selectAll(".linetext").style("fill-opacity", 0.1);
    //显示相关的连线文字
    _this.qaGraphLinkText
      .selectAll(".linktext")
      .style("fill-opacity", function(c) {
        if (c.lk.targetid === d.uuid || c.lk.sourceid === d.uuid) {
          return 1.0;
        }
      });
  } //展示与节点d相关的节点
  function switchButtonShow(d) {
    let _this = context;
    _this.selectUuid = d.uuid;
    var out_buttongroup_id = ".out_buttongroup_" + d.uuid;
    var selectItem = d3.select(out_buttongroup_id)._groups[0][0];
    if (selectItem.classList.contains("notshow")) {
      _this.svg.selectAll(".buttongroup").classed("notshow", true);
      d3.select(out_buttongroup_id)
        .classed("notshow", false)
        .style("visibility", "hidden");
      // .style("display", "none")
    } else {
      d3.select(out_buttongroup_id)
        .classed("notshow", true)
        .style("visibility", "visible");
      // .style("display", "block")
    }
    event.stopPropagation();
  }

  this.updateGraph = function(data) {
    var _this = this;
    let nodes = data.nodes;
    let links = data.links;
    //定义按钮组引用的use元素
    // _this.drawToolButton(nodes);
    // 更新节点
    // drawNode(nodes);
    // 更新节点文字
    // drawNodeText(nodes);
    // 更新按钮组
    // var graphNodeButtonGroup = _this.drawButtonGroup(nodes);
    // 更新连线 links
    let graphLink = _this.drawLink(links);
    // 更新连线文字
    let graphLinkText = _this.drawLinkText(links);
    _this.simulation
      .nodes(nodes)
      .alphaTarget(0)
      .alphaDecay(0.05)
      .on("tick", ticked);
    function ticked() {
      // 更新连线坐标
      graphLink
        .attr("x1", function(d) {
          return d.source.x;
        })
        .attr("y1", function(d) {
          return d.source.y;
        })
        .attr("x2", function(d) {
          return d.target.x;
        })
        .attr("y2", function(d) {
          return d.target.y;
        });
      // 刷新连接线上的文字位置
      graphLinkText
        .attr("x", function(d) {
          if (!d.source.x || !d.target.x) return 0;
          var x = (parseFloat(d.source.x) + parseFloat(d.target.x)) / 2;
          return x;
        })
        .attr("y", function(d) {
          if (!d.source.y || !d.target.y) return 0;
          var y = (parseFloat(d.source.y) + parseFloat(d.target.y)) / 2;
          return y;
        });
      // 更新节点坐标
      _this.graphNode
        .attr("cx", function(d) {
          return d.x;
        })
        .attr("cy", function(d) {
          return d.y;
        });
      // 更新节点操作按钮组坐标
      // graphNodeButtonGroup
      //   .attr("cx", function(d) {
      //     return d.x;
      //   })
      //   .attr("cy", function(d) {
      //     return d.y;
      //   })
      //   .attr("transform", function(d) {
      //     return "translate(" + d.x + "," + d.y + ") scale(1)";
      //   });
      // 更新文字坐标
      _this.graphNodeText
        .attr("x", function(d) {
          return d.x;
        })
        .attr("y", function(d) {
          return d.y;
        });
    }
    _this.simulation.force("link").links(links);
    _this.simulation.force(
      "center",
      d3.forceCenter(_this.width / 2, _this.height / 2)
    );
    _this.simulation.alpha(1).restart();
    // 鼠标滚轮缩放
    _this.zoom = d3
      .zoom()
      .scaleExtent([0.1, 4])
      .on("zoom", _this.zoomed);
    _this.svg.call(_this.zoom);
    // 静止双击缩放
    _this.svg.on("dblclick.zoom", null);
    //为按钮组绑定事件
    // _this.bindEventButtonGroup();
  };
  this.drawNode = function(nodes) {
    var _this = this;
    var node = this.qaGraphNode.selectAll("circle").data(nodes, function(d) {
      return d.id;
    });
    node.exit().remove();
    var nodeEnter = node.enter().append("circle");
    nodeEnter.on("click", function(d) {
      console.log("触发单击");
      _this.selectUuid = d.id;
      var out_buttongroup_id = ".out_buttongroup_" + d.id;
      var selectItem = d3.select(out_buttongroup_id)._groups[0][0];
      if (selectItem.classList.contains("notshow")) {
        _this.svg.selectAll(".buttongroup").classed("notshow", true);
        d3.select(out_buttongroup_id).classed("notshow", false);
      } else {
        d3.select(out_buttongroup_id).classed("notshow", true);
      }
      event.stopPropagation();
    });
    nodeEnter.on("dblclick", function(d) {
      console.log("触发双击:" + d);
      event.preventDefault();
    });
    nodeEnter.on("mouseenter", function() {
      console.log("鼠标移入");
      d3.select(this).style("stroke-width", "6");
    });
    nodeEnter.on("mouseleave", function() {
      console.log("鼠标移出");
      d3.select(this).style("stroke-width", 2);
      //todo其他节点和连线一并显示
      d3.select(".node").style("fill-opacity", 1);
      d3.select(".nodetext").style("fill-opacity", 1);
      d3.selectAll(".line").style("stroke-opacity", 1);
      d3.selectAll(".linetext").style("fill-opacity", 1);
    });
    nodeEnter.on("mouseover", function(d) {
      //todo鼠标放上去只显示相关节点，其他节点和连线隐藏
      d3.selectAll(".node").style("fill-opacity", 0.1);
      var relvantNodeIds = [];
      var relvantNodes = _this.graph.links.filter(function(n) {
        return n.sourceid == d.id || n.targetid == d.id;
      });
      relvantNodes.forEach(function(item) {
        relvantNodeIds.push(item.sourceid);
        relvantNodeIds.push(item.targetid);
      });
      //显示相关的节点
      _this.qaGraphNode.selectAll("circle").style("fill-opacity", function(c) {
        if (relvantNodeIds.indexOf(c.id) > -1) {
          return 1.0;
        }
      });
      //透明所有节点文字
      d3.selectAll(".nodetext").style("fill-opacity", 0.1);
      //显示相关的节点文字
      _this.qaGraphNodeText
        .selectAll("text")
        .style("fill-opacity", function(c) {
          if (relvantNodeIds.indexOf(c.id) > -1) {
            return 1.0;
          }
        });
      //透明所有连线
      d3.selectAll(".line").style("stroke-opacity", 0.1);
      //显示相关的连线
      _this.qaGraphLink.selectAll("line").style("stroke-opacity", function(c) {
        if (c.lk.targetid === d.id) {
          console.log(c);
          return 1.0;
        }
      });
      //透明所有连线文字
      d3.selectAll(".linetext").style("fill-opacity", 0.1);
      //显示相关的连线文字
      _this.qaGraphLinkText
        .selectAll(".linetext")
        .style("fill-opacity", function(c) {
          if (c.lk.targetid === d.id) {
            return 1.0;
          }
        });
    });
    nodeEnter.call(
      d3
        .drag()
        .on("start", _this.dragStarted)
        .on("drag", _this.dragged)
        .on("end", _this.dragEnded)
    );
    node = nodeEnter.merge(node).text(function(d) {
      return d.name;
    });
    node.style("stroke", function(d) {
      if (d.color) {
        return d.color;
      }
      return "#A4ED6C";
    });
    node.style("stroke-opacity", 0.6);
    node.attr("r", function(d) {
      if (d.r) return d.r;
      return _this.default.R;
    });
    node.attr("fill", function(d, i) {
      //创建圆形图像
      if (d.imgsrc) {
        var img_w = 77,
          img_h = 80;
        var defs = d3.selectAll("svg >defs");
        var catpattern = defs
          .append("pattern")
          .attr("id", "catpattern" + i)
          .attr("height", 1)
          .attr("width", 1);
        catpattern
          .append("image")
          .attr("x", -(img_w / 2 - d.r))
          .attr("y", -(img_h / 2 - d.r))
          .attr("width", img_w)
          .attr("height", img_h)
          .attr("xlink:href", d.imgsrc);
        return "url(#catpattern" + i + ")";
      } else {
        if (d.cur === "1") {
          return _this.colorList[0];
        } else {
          return _this.colorList[2];
        }
      }
    });
    node
      .append("title") // 为每个节点设置title
      .text(function(d) {
        if (d.name) {
          return d.name;
        }
        return "";
      });
    _this.graphNode = node;
  };
  this.drawNodeText = function(nodes) {
    var _this = this;
    var nodetext = this.qaGraphNodeText
      .selectAll("text")
      .data(nodes, function(d) {
        return d.id;
      });
    nodetext.exit().remove();
    var nodetextEnter = nodetext.enter().append("text");
    nodetextEnter.call(
      d3
        .drag()
        .on("start", _this.dragStarted)
        .on("drag", _this.dragged)
        .on("end", _this.dragEnded)
    );
    nodetext = nodetextEnter.merge(nodetext).text(function(d) {
      return d.name;
    });
    nodetext
      .style("fill", function() {
        return "#333";
      })
      .style("pointer-events", "none") //取消鼠标对文字的感知
      .style("font-weight", function(d) {
        if (!d.font_size) {
          // 根据半径计算字体大小
          d.font_size = Math.floor(
            ((d.r ? d.r : _this.default.R) * 2) / d.name.length - 1
          );
        }
        return Math.floor(900 / d.font_size + 500);
      })
      .attr("class", "nodetext")
      .attr("dy", function(d) {
        if (!d.font_size) {
          // 根据半径计算字体大小
          d.font_size = Math.floor(
            ((d.r ? d.r : _this.default.R) * 2) / d.name.length - 1
          );
        }
        return String(d.font_size / 2 - 2);
      })
      .attr("font-family", "Microsoft YaHei")
      .attr("font-size", function(d) {
        if (!d.font_size) {
          // 根据半径计算字体大小
          d.font_size = Math.floor(
            ((d.r ? d.r : _this.default.R) * 2) / d.name.length - 1
          );
        }
        return d.font_size;
      })
      .attr("text-anchor", "middle")
      .text(function(d) {
        return d.name;
      });
    nodetext
      .append("title") // 为每个节点设置title
      .text(function(d) {
        if (d.name) {
          return d.name;
        }
        return "";
      });
    return nodetext;
  };
  this.drawLink = function(links) {
    var _this = this;
    var link = this.qaGraphLink.selectAll("line").data(links, function(d) {
      return d.id;
    });
    link.exit().remove();
    var linkEnter = link
      .enter()
      .append("line")
      .attr("class", "link")
      .attr("stroke-width", 1)
      .attr("stroke", function() {
        return _this.colorList[2];
      })
      .attr("marker-end", "url(#arrow)"); // 箭头
    link = linkEnter.merge(link);
    return link;
  };
  this.drawLinkText = function(links) {
    var _this = this;
    var linktext = _this.qaGraphLinkText
      .selectAll("text")
      .data(links, function(d) {
        return d.id;
      });
    linktext.exit().remove();
    var linktextEnter = linktext
      .enter()
      .append("text")
      .attr("class", "linetext")
      .style("fill", "#875034")
      .style("font-size", "10px")
      .text(function(d) {
        return d.lk.name;
      });
    linktext = linktextEnter.merge(linktext).text(function(d) {
      return d.lk.name;
    });
    return linktext;
  };
  this.drawButtonGroup = function(nodes) {
    var _this = this;
    d3.selectAll('.nodebutton >g').remove();
    var nodebutton = _this.nodebuttonGroup
      .selectAll('.nodebutton')
      .data(nodes, function (d) {
        return d.id
      });
    nodebutton.exit().remove();
    var nodebuttonEnter = nodebutton
      .enter()
      .append('use') //  为每个节点组添加一个 use 子元素
      .attr('r', function (d) {
        if (!d.r) {
          return _this.default.R;
        }
        return d.r;
      })
      .attr('uuid', function (d) {//todo---
        return d.id;
      })
      .attr('xlink:href', function (d) {
        if (!d.r) {
          return '#out_circle_' + _this.default.R;
        }
        return '#out_circle_' + d.r;
      }) //  指定 use 引用的内容
      .attr('class', function (d) {
        return 'buttongroup out_buttongroup_' + d.id;
      })
      .classed('notshow', true);
    nodebutton = nodebuttonEnter.merge(nodebutton);
    return nodebutton;
  };
  this.drawToolButton = function() {
    var _this = this;
    //先删除所有为节点自定义的按钮组
    d3.selectAll('svg >defs').remove();
    var nodes = _this.graph.nodes;
    var pie = d3.pie().value(function(d) {
      return d.value; //处理数据，指定value作为计算比例的字段
    });
    var piedata = pie(_this.toolbarData);
    var nodeButtonGroup = this.svg.append('defs');
    var nodeRArr = [];
    nodes.forEach(function(m) {
      if (!m.r) {
        m.r = _this.default.R;
      }
      //按半径分别定义每种按钮组的图标
      if (nodeRArr.indexOf(m.r) == -1) {
        nodeRArr.push(m.r);
        var nbtng = nodeButtonGroup
          .append('g')
          .attr('id', 'out_circle_' + m.r) //为每一个节点定制一个按钮组，在画按钮组的时候为其指定该id
        var buttonGroupEnter = nbtng
          .selectAll('.buttongroup')
          .data(piedata)
          .enter()
          .append('g')
          .attr('class', function (d) {
            return 'action_' + d.data.code
          });
        var arc = d3
          .arc()
          .innerRadius(m.r)
          .outerRadius(m.r + 30);
        buttonGroupEnter
          .append('path')
          .attr('d', function (d) {
            return arc(d)
          })
          .attr('fill', '#E6A23C')
          .style('opacity', 0.6)
          .attr('stroke', '#6CB7ED')
          .attr('stroke-width', 1)
        buttonGroupEnter
          .append('text')
          .attr('transform', function (d) {
            return 'translate(' + arc.centroid(d) + ')'
          })
          .attr('text-anchor', 'middle')
          .text(function (d) {
            return d.data.name;
          })
          .style('fill', function () {
            return '#fff'
          })
          .attr('font-size', 10)
      }
    });
  };
  this.formatData = function() {
    var _this = this;
    var lks = _this.graph.links;
    var nodes = _this.graph.nodes;
    nodes.forEach(function(n) {
      if (n.center === 1 || n.center === "1") {
        n.fx = _this.width / 2;
        n.fy = _this.height / 2;
      }
      if (typeof n.fx === "undefined" || n.fx === "") {
        n.fx = null;
      }
      if (typeof n.fy === "undefined" || n.fy === "") {
        n.fy = null;
      }
    });
    var links = [];
    lks.forEach(function(m) {
      var sourceNode = nodes.filter(function(n) {
        return n.id === m.sourceid;
      })[0];
      if (!sourceNode) return;
      var targetNode = nodes.filter(function(n) {
        return n.id === m.targetid;
      })[0];
      if (!targetNode) return;
      links.push({ source: sourceNode.id, target: targetNode.id, lk: m });
    });
    var data = {};
    data.nodes = nodes;
    data.links = _this.formatLinkAttr(links);
    return data;
  };
  this.formatLinkAttr = function(links) {
    var formatLinks = [];
    //每两个节点间的连线，不管正向反向，全算一组，每组连线分别标上序号linkGroupIndex，计算每组连线总条数groupLinkTotal
    links.forEach(function(item) {
      var linkGroup = [];
      //正向
      var forwardItems = links.filter(function(sItem) {
        return sItem.source == item.source && sItem.target == item.target;
      });
      //反向
      var reverseItems = links.filter(function(sItem) {
        return sItem.source == item.target && sItem.target == item.source;
      });
      linkGroup = linkGroup.concat(forwardItems);
      linkGroup = linkGroup.concat(reverseItems);
      var groupLinkTotal = forwardItems.length + reverseItems.length;
      linkGroup.forEach(function(it, i) {
        //本组连线序号
        it.linkGroupIndex = i + 1;
        it.groupLinkTotal = groupLinkTotal;
      });
      formatLinks = formatLinks.concat(linkGroup);
    });
    formatLinks.forEach(function(item) {
      //线的单双，单数且小于2，则只有一条直线，大于2且单数，有多条弧线和一条直线
      var groupLinkTotalHalf = item.groupLinkTotal / 2;
      //能否被2整除，是否有余数，true=有，false=没有，有余数必然有一条直线
      item.groupLinkUneven = item.groupLinkTotal % 2 !== 0;
      //判断是否是最中间的直线（有余数，并且序号刚好是本组连线的一版，这里向上取整）
      item.groupLinkMiddle =
        item.groupLinkUneven === true &&
        Math.ceil(groupLinkTotalHalf) === item.linkGroupIndex;
      //假设有一条直线，index刚好是groupLinkTotalHalf的值，上方的曲线都为0，下方的曲线都为1
      var groupLinkLowerHalf = item.linkGroupIndex <= groupLinkTotalHalf;
      //上方的曲线和直线方向一致，下方的曲线和直线的方向相反
      //item.groupLinkArcDirection = groupLinkLowerHalf ? 0 : 1
      item.groupLinkArcDirection = 1;
      //都在直线上方，则取当前序号，否则减去中间直线取序号
      item.groupLinkIndexCorrected = groupLinkLowerHalf
        ? item.linkGroupIndex
        : item.linkGroupIndex - Math.ceil(groupLinkTotalHalf);
      item.groupLinkMaxHalf = Math.round(item.groupLinkTotal / 2);
    });
    return formatLinks;
  };
  this.zoomed = function() {
    d3.selectAll('.node').attr('transform', d3.event.transform)
    d3.selectAll('.nodetext text').attr('transform', d3.event.transform)
    d3.selectAll('.line').attr('transform', d3.event.transform)
    d3.selectAll('.linetext text').attr('transform', d3.event.transform)
    d3.selectAll('.nodebutton').attr('transform', d3.event.transform)
    //_this.svg.selectAll("g").attr("transform", d3.event.transform);
  };
  this.zoomClick = function(direction) {
    var self = this
    var factor = 0.2
    var targetZoom = 1
    var extent = self.zoom.scaleExtent()
    targetZoom = 1 + factor * direction
    if (targetZoom < extent[0] || targetZoom > extent[1]) {
      return false
    }
    self.zoom.scaleBy(self.svg, targetZoom) // 执行该方法后 会触发zoom事件
  };
  this.zoomIn = function() {
    this.zoomClick(1)
  };
  this.zoomOut = function() {
    this.zoomClick(-1)
  };
  this.bindEventButtonGroup = function() {
    var _this = this;
    //按钮组事件绑定
    _this.toolbarData.forEach(function(m) {
      var btnClass = '.action_' + m.code;
      _this.svg.selectAll(btnClass).on('click', function (d) {
        console.log(
          d.data.name + ':' + d.data.code + ':uuid:' + _this.selectUuid //todo--
        );
      });
    });
  };
  this.refresh = function() {
    this.svg.call(this.zoom.transform, d3.zoomIdentity);
  };
}
